package com.example.demo.thread.ch5.case08;

import com.example.demo.thread.util.Debug;
import com.example.demo.thread.util.Tools;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * CyclicBarrier使用示例
 * <p>
 * 模拟士兵打靶训练。所有参与训练的士兵被分为若干组，其中每组被称为一排。
 * 一排中士兵的个数等于靶子的个数。每次只能够有一排士兵进行射击。
 * 一排中的士兵必须同时开始射击，并且射击完毕的士兵必须等待同排的其他所有士兵射击完毕后才能整排撤离射击点
 *
 * @author: htc
 * @date: Created in 15:05 2019/1/14.
 */
public class ShootPractice {
    //参与打靶训练的全部士兵
    final Soldier[][] rank;

    //靶的个数，即每排中士兵的个数
    final int N;

    //打靶训练持续时间(s)
    final int lasting;

    //标识是否继续打靶
    volatile boolean done = false;

    //用来指示进行下一轮打靶的是哪一个排的士兵
    volatile int nextLine = 0;

    final CyclicBarrier shiftBarrier;
    final CyclicBarrier startBarrier;

    public ShootPractice(int N, final int lineCount, int lasting) {
        this.N = N;
        this.lasting = lasting;
        this.rank = new Soldier[lineCount][N];
        for (int i = 0; i < lineCount; i++) {
            for (int j = 0; j < N; j++) {
                rank[i][j] = new Soldier(i * N + j);
            }
        }
        shiftBarrier = new CyclicBarrier(N, new Runnable() {
            @Override
            public void run() {
                //更新下一轮打靶的排
                //语句1
                nextLine = (nextLine + 1) % lineCount;
                Debug.info("Next turn is :%d", nextLine);
            }
        });
        //语句2
        startBarrier = new CyclicBarrier(N);
    }

    class Shooting extends Thread {
        final int index;

        public Shooting(int index) {
            this.index = index;
        }

        @Override
        public void run() {
            Soldier soldier;
            try {
                while (!done) {
                    soldier = rank[nextLine][index];
                    //一排中的士兵必须同时开始射击
                    //语句3
                    startBarrier.await();

                    //该士兵开始射击
                    soldier.fire();

                    //一排中的士兵必须等待该排中的其他所有士兵射击完毕才能够离开射击点
                    //语句4
                    shiftBarrier.await();

                }
            } catch (InterruptedException e) {
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            } finally {
            }
        }
    }

    /**
     * 参与打靶训练的士兵
     */
    static class Soldier {
        private final int seqNo;

        public Soldier(int seqNo) {
            this.seqNo = seqNo;
        }

        public void fire() {
            Debug.info(this + " start firing...");
            Tools.randomPause(5000);
            System.out.println(this + " fired.");
        }

        @Override
        public String toString() {
            return "Soldier-" + seqNo;
        }
    }

    public void start() throws InterruptedException {
        //创建并启动工作者线程
        Thread[] threads = new Thread[N];
        for (int i = 0; i < N; i++) {
            threads[i] = new Shooting(i);
            threads[i].start();
        }
        //指定时间后停止打靶
        Thread.sleep(lasting * 1000);
        stop();
        for (Thread t : threads) {
            t.join();
        }
        Debug.info("Practice finished!");
    }

    public void stop() {
        done = true;
    }

    public static void main(String[] args) throws InterruptedException {
        ShootPractice sp = new ShootPractice(4, 5, 24);
        sp.start();
    }
}
